home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / FLTK-1.0.6 / src / Fl_Group.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1999-08-09  |  13.2 KB  |  508 lines

  1. //
  2. // "$Id: Fl_Group.cxx,v 1.8.2.3 1999/08/09 06:19:32 bill Exp $"
  3. //
  4. // Group widget for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 1998-1999 by Bill Spitzak and others.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems to "fltk-bugs@easysw.com".
  24. //
  25.  
  26. // The Fl_Group is the only defined container type in fltk.
  27.  
  28. // Fl_Window itself is a subclass of this, and most of the event
  29. // handling is designed so windows themselves work correctly.
  30.  
  31. #include <FL/Fl.H>
  32. #include <FL/Fl_Group.H>
  33. #include <FL/Fl_Window.H>
  34. #include <FL/fl_draw.H>
  35. #include <stdlib.h>
  36.  
  37. Fl_Group* Fl_Group::current_;
  38.  
  39. // Hack: A single child is stored in the pointer to the array, while
  40. // multiple children are stored in an allocated array:
  41. Fl_Widget*const* Fl_Group::array() const {
  42.   return children_ <= 1 ? (Fl_Widget**)(&array_) : array_;
  43. }
  44.  
  45. int Fl_Group::find(const Fl_Widget* o) const {
  46.   Fl_Widget*const* a = array();
  47.   int i; for (i=0; i < children_; i++) if (*a++ == o) break;
  48.   return i;
  49. }
  50.  
  51. extern Fl_Widget* fl_oldfocus; // set by Fl::focus
  52.  
  53. // For back-compatability, we must adjust all events sent to child
  54. // windows so they are relative to that window.
  55.  
  56. static int send(Fl_Widget* o, int event) {
  57.   if (o->type() < FL_WINDOW) return o->handle(event);
  58.   int save_x = Fl::e_x; Fl::e_x -= o->x();
  59.   int save_y = Fl::e_y; Fl::e_y -= o->y();
  60.   int ret = o->handle(event);
  61.   Fl::e_y = save_y;
  62.   Fl::e_x = save_x;
  63.   return ret;
  64. }
  65.  
  66. // translate the current keystroke into up/down/left/right for navigation:
  67. #define ctrl(x) (x^0x40)
  68. static int navkey() {
  69.   switch (Fl::event_key()) {
  70.   case FL_Tab:
  71.     if (!Fl::event_state(FL_SHIFT)) return FL_Right;
  72.   case 0xfe20: // XK_ISO_Left_Tab
  73.     return FL_Left;
  74.   case FL_Right:
  75.     return FL_Right;
  76.   case FL_Left:
  77.     return FL_Left;
  78.   case FL_Up:
  79.     return FL_Up;
  80.   case FL_Down:
  81.     return FL_Down;
  82.   default:
  83.     switch (Fl::event_text()[0]) {
  84.     case ctrl('N') : return FL_Down;
  85.     case ctrl('P') : return FL_Up;
  86.     case ctrl('F') : return FL_Right;
  87.     case ctrl('B') : return FL_Left;
  88.     }
  89.   }
  90.   return 0;
  91. }
  92.  
  93. int Fl_Group::handle(int event) {
  94.  
  95.   Fl_Widget*const* a = array();
  96.   int i;
  97.   Fl_Widget* o;
  98.  
  99.   switch (event) {
  100.  
  101.   case FL_FOCUS:
  102.     switch (navkey()) {
  103.     default:
  104.       if (savedfocus_ && savedfocus_->take_focus()) return 1;
  105.     case FL_Right:
  106.     case FL_Down:
  107.       for (i = children(); i--;) if ((*a++)->take_focus()) return 1;
  108.       break;
  109.     case FL_Left:
  110.     case FL_Up:
  111.       for (i = children(); i--;) if (a[i]->take_focus()) return 1;
  112.       break;
  113.     }
  114.     return 0;
  115.  
  116.   case FL_UNFOCUS:
  117.     savedfocus_ = fl_oldfocus;
  118.     return 0;
  119.  
  120.   case FL_KEYBOARD:
  121.     return navigation(navkey());
  122.  
  123.   case FL_SHORTCUT:
  124.     for (i = children(); i--;) {
  125.       o = a[i];
  126.       if (o->takesevents() && Fl::event_inside(o) && send(o,FL_SHORTCUT))
  127.     return 1;
  128.     }
  129.     for (i = children(); i--;) {
  130.       o = a[i];
  131.       if (o->takesevents() && !Fl::event_inside(o) && send(o,FL_SHORTCUT))
  132.     return 1;
  133.     }
  134.     if (Fl::event_key() == FL_Enter) return navigation(FL_Down);
  135.     return 0;
  136.  
  137.   case FL_ENTER:
  138.   case FL_MOVE:
  139.     for (i = children(); i--;) {
  140.       o = a[i];
  141.       if (o->takesevents() && Fl::event_inside(o)) {
  142.     if (o->contains(Fl::belowmouse())) {
  143.       return send(o,FL_MOVE);
  144.     } else if (send(o,FL_ENTER)) {
  145.       if (!o->contains(Fl::belowmouse())) Fl::belowmouse(o);
  146.       return 1;
  147.     }
  148.       }
  149.     }
  150.     Fl::belowmouse(this);
  151.     return 1;
  152.  
  153.   case FL_PUSH:
  154.     for (i = children(); i--;) {
  155.       o = a[i];
  156.       if (o->takesevents() && Fl::event_inside(o)) {
  157.     if (send(o,FL_PUSH)) {
  158.       if (Fl::pushed() && !o->contains(Fl::pushed())) Fl::pushed(o);
  159.       return 1;
  160.     }
  161.       }
  162.     }
  163.     return 0;
  164.  
  165.   case FL_DEACTIVATE:
  166.   case FL_ACTIVATE:
  167.     for (i = children(); i--;) {
  168.       o = *a++;
  169.       if (o->active()) o->handle(event);
  170.     }
  171.     return 1;
  172.  
  173.   case FL_SHOW:
  174.   case FL_HIDE:
  175.     for (i = children(); i--;) {
  176.       o = *a++;
  177.       if (o->visible()) o->handle(event);
  178.     }
  179.     return 1;
  180.  
  181.   default:
  182.     return 0;
  183.  
  184.   }
  185. }
  186.  
  187. //void Fl_Group::focus(Fl_Widget *o) {Fl::focus(o); o->handle(FL_FOCUS);}
  188.  
  189. #if 0
  190. const char *nameof(Fl_Widget *o) {
  191.   if (!o) return "NULL";
  192.   if (!o->label()) return "<no label>";
  193.   return o->label();
  194. }
  195. #endif
  196.  
  197. // try to move the focus in response to a keystroke:
  198. int Fl_Group::navigation(int key) {
  199.   if (children() <= 1) return 0;
  200.   int i;
  201.   for (i = 0; ; i++) {
  202.     if (i >= children_) return 0;
  203.     if (array_[i]->contains(Fl::focus())) break;
  204.   }
  205.   Fl_Widget *previous = array_[i];
  206.  
  207.   for (;;) {
  208.     switch (key) {
  209.     case FL_Right:
  210.     case FL_Down:
  211.       i++;
  212.       if (i >= children_) {
  213.     if (parent()) return 0;
  214.     i = 0;
  215.       }
  216.       break;
  217.     case FL_Left:
  218.     case FL_Up:
  219.       if (i) i--;
  220.       else {
  221.     if (parent()) return 0;
  222.     i = children_-1;
  223.       }
  224.       break;
  225.     default:
  226.       return 0;
  227.     }
  228.     Fl_Widget* o = array_[i];
  229.     if (o == previous) return 0;
  230.     switch (key) {
  231.     case FL_Down:
  232.     case FL_Up:
  233.       // for up/down, the widgets have to overlap horizontally:
  234.       if (o->x() >= previous->x()+previous->w() ||
  235.       o->x()+o->w() <= previous->x()) continue;
  236.     }
  237.     if (o->take_focus()) return 1;
  238.   }
  239. }
  240.  
  241. ////////////////////////////////////////////////////////////////
  242.  
  243. Fl_Group::Fl_Group(int X,int Y,int W,int H,const char *l)
  244. : Fl_Widget(X,Y,W,H,l) {
  245.   align(FL_ALIGN_TOP);
  246.   children_ = 0;
  247.   array_ = 0;
  248.   savedfocus_ = 0;
  249.   resizable_ = this;
  250.   sizes_ = 0; // this is allocated when first resize() is done
  251.   // Subclasses may want to construct child objects as part of their
  252.   // constructor, so make sure they are add()'d to this object.
  253.   // But you must end() the object!
  254.   begin();
  255. }
  256.  
  257. void Fl_Group::clear() {
  258.   Fl_Widget*const* old_array = array();
  259.   int old_children = children();
  260.   // clear everything now, in case fl_fix_focus recursively calls us:
  261.   children_ = 0;
  262.   // array_ = 0; dont do this, it will clobber old_array if only one child
  263.   savedfocus_ = 0;
  264.   resizable_ = this;
  265.   init_sizes();
  266.   // okay, now it is safe to destroy the children:
  267.   Fl_Widget*const* a = old_array;
  268.   for (int i=old_children; i--;) {
  269.     Fl_Widget* o = *a++;
  270.     // test the parent to see if child already destructed:
  271.     if (o->parent() == this) delete o;
  272.   }
  273.   if (old_children > 1) free((void*)old_array);
  274. }
  275.  
  276. Fl_Group::~Fl_Group() {clear();}
  277.  
  278. void Fl_Group::insert(Fl_Widget &o, int i) {
  279.   if (o.parent()) ((Fl_Group*)(o.parent()))->remove(o);
  280.   o.parent_ = this;
  281.   if (children_ == 0) { // use array pointer to point at single child
  282.     array_ = (Fl_Widget**)&o;
  283.   } else if (children_ == 1) { // go from 1 to 2 children
  284.     Fl_Widget* t = (Fl_Widget*)array_;
  285.     array_ = (Fl_Widget**)malloc(2*sizeof(Fl_Widget*));
  286.     if (i) {array_[0] = t; array_[1] = &o;}
  287.     else {array_[0] = &o; array_[1] = t;}
  288.   } else {
  289.     if (!(children_ & (children_-1))) // double number of children
  290.       array_ = (Fl_Widget**)realloc((void*)array_,
  291.                     2*children_*sizeof(Fl_Widget*));
  292.     int j; for (j = children_; j > i; j--) array_[j] = array_[j-1];
  293.     array_[j] = &o;
  294.   }
  295.   children_++;
  296.   init_sizes();
  297. }
  298.  
  299. void Fl_Group::add(Fl_Widget &o) {insert(o, children_);}
  300.  
  301. void Fl_Group::remove(Fl_Widget &o) {
  302.   int i = find(o);
  303.   if (i >= children_) return;
  304.   if (&o == savedfocus_) savedfocus_ = 0;
  305.   o.parent_ = 0;
  306.   children_--;
  307.   if (children_ == 1) { // go from 2 to 1 child
  308.     Fl_Widget *t = array_[!i];
  309.     free((void*)array_);
  310.     array_ = (Fl_Widget**)t;
  311.   } else if (children_ > 1) { // delete from array
  312.     for (; i < children_; i++) array_[i] = array_[i+1];
  313.   }
  314.   init_sizes();
  315. }
  316.  
  317. ////////////////////////////////////////////////////////////////
  318.  
  319. // Rather lame kludge here, I need to detect windows and ignore the
  320. // changes to X,Y, since all children are relative to X,Y.  That
  321. // is why I check type():
  322.  
  323. // sizes array stores the initial positions of widgets as
  324. // left,right,top,bottom quads.  The first quad is the group, the
  325. // second is the resizable (clipped to the group), and the
  326. // rest are the children.  This is a convienent order for the
  327. // algorithim.  If you change this be sure to fix Fl_Tile which
  328. // also uses this array!
  329.  
  330. void Fl_Group::init_sizes() {
  331.   delete[] sizes_; sizes_ = 0;
  332. }
  333.  
  334. short* Fl_Group::sizes() {
  335.   if (!sizes_) {
  336.     short* p = sizes_ = new short[4*(children_+2)];
  337.     // first thing in sizes array is the group's size:
  338.     if (type() < FL_WINDOW) {p[0] = x(); p[2] = y();} else {p[0] = p[2] = 0;}
  339.     p[1] = p[0]+w(); p[3] = p[2]+h();
  340.     // next is the resizable's size:
  341.     p[4] = p[0]; // init to the group's size
  342.     p[5] = p[1];
  343.     p[6] = p[2];
  344.     p[7] = p[3];
  345.     Fl_Widget* r = resizable();
  346.     if (r && r != this) { // then clip the resizable to it
  347.       int t;
  348.       t = r->x(); if (t > p[0]) p[4] = t;
  349.       t +=r->w(); if (t < p[1]) p[5] = t;
  350.       t = r->y(); if (t > p[2]) p[6] = t;
  351.       t +=r->h(); if (t < p[3]) p[7] = t;
  352.     }
  353.     // next is all the children's sizes:
  354.     p += 8;
  355.     Fl_Widget*const* a = array();
  356.     for (int i=children_; i--;) {
  357.       Fl_Widget* o = *a++;
  358.       *p++ = o->x();
  359.       *p++ = o->x()+o->w();
  360.       *p++ = o->y();
  361.       *p++ = o->y()+o->h();
  362.     }
  363.   }
  364.   return sizes_;
  365. }
  366.  
  367. void Fl_Group::resize(int X, int Y, int W, int H) {
  368.  
  369.   if (!resizable() || W==w() && H==h()) {
  370.  
  371.     if (type() < FL_WINDOW) {
  372.       int dx = X-x();
  373.       int dy = Y-y();
  374.       Fl_Widget*const* a = array();
  375.       for (int i=children_; i--;) {
  376.     Fl_Widget* o = *a++;
  377.     o->resize(o->x()+dx, o->y()+dy, o->w(), o->h());
  378.       }
  379.     }
  380.  
  381.   } else if (children_) {
  382.  
  383.     short* p = sizes();
  384.  
  385.     // get changes in size/position from the initial size:
  386.     int dx = X - p[0];
  387.     int dw = W - (p[1]-p[0]);
  388.     int dy = Y - p[2];
  389.     int dh = H - (p[3]-p[2]);
  390.     if (type() >= FL_WINDOW) dx = dy = 0;
  391.     p += 4;
  392.  
  393.     // get initial size of resizable():
  394.     int IX = *p++;
  395.     int IR = *p++;
  396.     int IY = *p++;
  397.     int IB = *p++;
  398.  
  399.     Fl_Widget*const* a = array();
  400.     for (int i=children_; i--;) {
  401.       Fl_Widget* o = *a++;
  402. #if 1
  403.       int X = *p++;
  404.       if (X >= IR) X += dw;
  405.       else if (X > IX) X = IX+((X-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX);
  406.       int R = *p++;
  407.       if (R >= IR) R += dw;
  408.       else if (R > IX) R = IX+((R-IX)*(IR+dw-IX)+(IR-IX)/2)/(IR-IX);
  409.  
  410.       int Y = *p++;
  411.       if (Y >= IB) Y += dh;
  412.       else if (Y > IY) Y = IY+((Y-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY);
  413.       int B = *p++;
  414.       if (B >= IB) B += dh;
  415.       else if (B > IY) B = IY+((B-IY)*(IB+dh-IY)+(IB-IY)/2)/(IB-IY);
  416. #else // much simpler code from Francois Ostiguy:
  417.       int X = *p++;
  418.       if (X >= IR) X += dw;
  419.       else if (X > IX) X = X + dw * (X-IX)/(IR-IX);
  420.       int R = *p++;
  421.       if (R >= IR) R += dw;
  422.       else if (R > IX) R = R + dw * (R-IX)/(IR-IX);
  423.  
  424.       int Y = *p++;
  425.       if (Y >= IB) Y += dh;
  426.       else if (Y > IY) Y = Y + dh*(Y-IY)/(IB-IY);
  427.       int B = *p++;
  428.       if (B >= IB) B += dh;
  429.       else if (B > IY) B = B + dh*(B-IY)/(IB-IY);
  430. #endif
  431.       o->resize(X+dx, Y+dy, R-X, B-Y);
  432.     }
  433.   }
  434.  
  435.   Fl_Widget::resize(X,Y,W,H);
  436. }
  437.  
  438. void Fl_Group::draw() {
  439.   Fl_Widget*const* a = array();
  440.   if (damage() & ~FL_DAMAGE_CHILD) { // redraw the entire thing:
  441.     draw_box();
  442.     draw_label();
  443.     for (int i=children_; i--;) {
  444.       Fl_Widget& o = **a++;
  445.       draw_child(o);
  446.       draw_outside_label(o);
  447.     }
  448.   } else {    // only redraw the children that need it:
  449.     for (int i=children_; i--;) update_child(**a++);
  450.   }
  451. }
  452.  
  453. // Draw a child only if it needs it:
  454. void Fl_Group::update_child(Fl_Widget& w) const {
  455.   if (w.damage() && w.visible() && w.type() < FL_WINDOW &&
  456.       fl_not_clipped(w.x(), w.y(), w.w(), w.h())) {
  457.     w.draw();    
  458.     w.clear_damage();
  459.   }
  460. }
  461.  
  462. // Force a child to redraw:
  463. void Fl_Group::draw_child(Fl_Widget& w) const {
  464.   if (w.visible() && w.type() < FL_WINDOW &&
  465.       fl_not_clipped(w.x(), w.y(), w.w(), w.h())) {
  466.     w.clear_damage(FL_DAMAGE_ALL);
  467.     w.draw();
  468.     w.clear_damage();
  469.   }
  470. }
  471.  
  472. extern char fl_draw_shortcut;
  473.  
  474. // Parents normally call this to draw outside labels:
  475. void Fl_Group::draw_outside_label(const Fl_Widget& w) const {
  476.   if (!w.visible()) return;
  477.   // skip any labels that are inside the widget:
  478.   if (!(w.align()&15) || (w.align() & FL_ALIGN_INSIDE)) return;
  479.   // invent a box that is outside the widget:
  480.   int align = w.align();
  481.   int X = w.x();
  482.   int Y = w.y();
  483.   int W = w.w();
  484.   int H = w.h();
  485.   if (align & FL_ALIGN_TOP) {
  486.     align ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP);
  487.     Y = y();
  488.     H = w.y()-Y;
  489.   } else if (align & FL_ALIGN_BOTTOM) {
  490.     align ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP);
  491.     Y = Y+H;
  492.     H = y()+h()-Y;
  493.   } else if (align & FL_ALIGN_LEFT) {
  494.     align ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT);
  495.     X = x();
  496.     W = w.x()-X-3;
  497.   } else if (align & FL_ALIGN_RIGHT) {
  498.     align ^= (FL_ALIGN_LEFT|FL_ALIGN_RIGHT);
  499.     X = X+W+3;
  500.     W = x()+this->w()-X;
  501.   }
  502.   w.draw_label(X,Y,W,H,(Fl_Align)align);
  503. }
  504.  
  505. //
  506. // End of "$Id: Fl_Group.cxx,v 1.8.2.3 1999/08/09 06:19:32 bill Exp $".
  507. //
  508.